package org.piggy.client.core.scanner;

import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.piggy.client.core.ApiInvoker;
import org.piggy.client.core.ApiProtocol;
import org.piggy.client.core.ApiService;
import org.piggy.common.constants.DubboConstants;
import org.piggy.common.invoker.BasicServiceInvoker;
import org.piggy.common.invoker.DubboServiceInvoker;
import org.piggy.common.config.ServiceDefinition;
import org.piggy.common.invoker.ServiceInvoker;
import org.piggy.common.constants.BasicConst;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ApiAnnotationScanner {

    private ApiAnnotationScanner() {}

    private static class SingletonHolder {
        static final ApiAnnotationScanner INSTANCE = new ApiAnnotationScanner();
    }

    public static ApiAnnotationScanner getInstance() {
        return SingletonHolder.INSTANCE;
    }


    public ServiceDefinition scanner(Object bean, Object... args) {
        Class<?> clz = bean.getClass();
        if (!clz.isAnnotationPresent(ApiService.class)) {
            return null;
        }

        Method[] methods = clz.getMethods();
        if (methods.length > 0) {
            ApiService apiService = clz.getAnnotation(ApiService.class);
            String serviceId = apiService.serviceId();
            ApiProtocol protocol = apiService.protocol();
            String version = apiService.version();
            String patternPath = apiService.patternPath();

            ServiceDefinition serviceDefinition = new ServiceDefinition();
            Map<String, ServiceInvoker> invokerMap = new HashMap<>();
            for (Method method : methods) {
                ApiInvoker apiInvoker = method.getAnnotation(ApiInvoker.class);
                if (apiInvoker == null) {
                    continue;
                }

                String path = apiInvoker.path();

                switch (protocol) {
                    case HTTP -> {
                        BasicServiceInvoker httpServiceInvoker = createHttpServiceInvoker(path);
                        invokerMap.put(path, httpServiceInvoker);
                    }
                    case DUBBO -> {
                        ServiceBean<?> serviceBean = (ServiceBean<?>) args[0];
                        DubboServiceInvoker dubboServiceInvoker = createDubboServiceInvoker(path, serviceBean, method);
                        String dubboVersion = dubboServiceInvoker.getVersion();
                        if (!StringUtils.isBlank(dubboVersion)) {
                            version = dubboVersion;
                        }
                        invokerMap.put(path, dubboServiceInvoker);
                    }
                    default -> throw new RuntimeException("协议错误");
                }
            }

            serviceDefinition.setServiceId(serviceId);
            serviceDefinition.setVersion(version);
            String serviceName = serviceId + BasicConst.COLON_SEPARATOR + version;
            serviceDefinition.setServiceName(serviceName);
            serviceDefinition.setProtocol(protocol.getCode());
            serviceDefinition.setEnable(true);
            serviceDefinition.setInvokerMap(invokerMap);
            serviceDefinition.setPatternPath(patternPath);

            return serviceDefinition;
        }
        return null;
    }

    private BasicServiceInvoker createHttpServiceInvoker(String path) {
        BasicServiceInvoker httpServiceInvoker = new BasicServiceInvoker();
        httpServiceInvoker.setInvokerPath(path);
        return httpServiceInvoker;
    }

    private DubboServiceInvoker createDubboServiceInvoker(String path, ServiceBean<?> serviceBean, Method method) {
        DubboServiceInvoker dubboServiceInvoker = new DubboServiceInvoker();
        dubboServiceInvoker.setInvokerPath(path);

        String methodName = method.getName();
        String registerAddress = serviceBean.getRegistry().getAddress();
        String interfaceClass = serviceBean.getInterface();
        String name = serviceBean.getApplication().getName();
        dubboServiceInvoker.setApplicationName(name);
        dubboServiceInvoker.setRegisterAddress(registerAddress);
        dubboServiceInvoker.setMethodName(methodName);
        dubboServiceInvoker.setInterfaceClass(interfaceClass);

        int parameterCount = method.getParameterCount();
        if(parameterCount != 0){
            String[] parameterTypes = new String[parameterCount];
            Class<?>[] classes = method.getParameterTypes();
            for (int i = 0; i < classes.length; i++) {
                parameterTypes[i] = classes[i].getName();
            }
            dubboServiceInvoker.setParameterTypes(parameterTypes);
        }

        Integer seriveTimeout = serviceBean.getTimeout();
        if (seriveTimeout == null || seriveTimeout == 0) {
            seriveTimeout = DubboConstants.DUBBO_TIMEOUT;
            ProviderConfig providerConfig = serviceBean.getProvider();
            if (providerConfig != null) {
                Integer providerTimeout = providerConfig.getTimeout();
                if (providerTimeout != null && providerTimeout != 0) {
                    seriveTimeout = providerTimeout;
                }
            }
        }
        dubboServiceInvoker.setTimeout(seriveTimeout);

        String dubboVersion = serviceBean.getVersion();
        dubboServiceInvoker.setVersion(dubboVersion);

        return dubboServiceInvoker;
    }

}
